use super::*;

/// Represents a JavaScript DataView object
#[derive(Clone, Copy)]
pub struct DataView<'val> {
	env: &'val Env,
	inner: sys::napi_value,
}

impl<'val> DataView<'val> {
	/// Creates a DataView from a raw napi_value
	pub fn with_raw<'env>(env: &'env Env, inner: sys::napi_value) -> DataView<'env> {
		DataView { env, inner }
	}

	/// Creates a new DataView from an ArrayBuffer
	pub fn new<'env>(env: &'env Env, arraybuffer: ArrayBuffer<'env>, byte_offset: usize, length: usize) -> Result<'static, DataView<'env>> {
		let mut ret = core::ptr::null_mut();
		unsafe { sys::napi_create_dataview(env.raw(), length, arraybuffer.inner, byte_offset, &mut ret) }.result("napi_create_dataview")?;
		Ok(DataView::with_raw(env, ret))
	}

	/// Converts DataView to a generic Value
	pub fn v(&'_ self) -> Value<'val> {
		Value::new(self.env, self.inner)
	}

	/// Gets detailed information about the DataView
	pub fn info(&'_ self) -> Result<'static, (ArrayBuffer<'val>, &'val [u8], usize)> {
		let mut arraybuffer = core::ptr::null_mut();
		let mut data = core::ptr::null_mut();
		let mut byte_offset = 0;
		let mut length = 0;
		unsafe {
			sys::napi_get_dataview_info(
				self.env.raw(), //
				self.inner,
				&mut length,
				&mut data,
				&mut arraybuffer,
				&mut byte_offset,
			)
		}
		.result("napi_get_dataview_info")?;
		Ok((
			ArrayBuffer::with_raw(self.env, arraybuffer),
			unsafe { core::slice::from_raw_parts(data as *const u8, length) },
			byte_offset,
		))
	}
}

impl<'val> IntoNapi for DataView<'val> {
	/// Converts from DataView to Value
	fn into_napi<'env>(self, env: &'env Env) -> std::result::Result<value::Value<'env>, status::Status<'static>> {
		if self.env.raw() != env.raw() {
			return Err(Status::invalid_arg("self.env not eq arg env"));
		}
		Ok(Value::new(env, self.inner))
	}
}

impl<'val> FromNapi<'val> for DataView<'val> {
	/// Converts from Value to DataView
	fn from_napi(v: Value<'val>) -> Result<'static, Self> {
		if !v.is_dataview()? {
			return Err(Status::invalid_arg("value is not a dataview"));
		}
		Ok(DataView::with_raw(v.env, v.inner))
	}
}
